﻿using CojectoSample.Common;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

using Windows.Storage;
using System.Threading.Tasks;
using OrandoLabs.Common;
using OrandoLabs.EnduroStore;
using OrandoLabs.CojectoSemantic;

// The Grid App template is documented at http://go.microsoft.com/fwlink/?LinkId=234226

namespace CojectoSample
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        EnduroDatabase Database;
        OrandoInstance Instance;
        EnduroInstance EInstance;
        Exception CojectoException;
        CojectoSemanticDatabase SemanticDatabase;
        String CojectoDirName = "cojecto-dir";
        String CojectoInstanceName = "cojecto";
        String CojectoDatabaseName = "cojecto.edb";
        private async Task<bool> InitializeCojectoAsync()
        {
            CojectoException = null;
            var appData = Windows.Storage.ApplicationData.Current;
            var path = appData.LocalFolder.CreateFolderAsync(CojectoDirName, CreationCollisionOption.OpenIfExists);
            var folder = await path.AsTask<Windows.Storage.StorageFolder>();
            var baseDir = folder.Path;

            return await Task.Run(() =>
            {
                bool inner = true;
                try
                {
                    Instance = new OrandoInstance(CojectoInstanceName, baseDir);
                    EInstance = new EnduroInstance(Instance);
                    Database = new EnduroDatabase(EInstance, CojectoDatabaseName);
                    SemanticDatabase = new CojectoSemanticDatabase("cojecto", Database);
                }
                catch (Exception e)
                {
                    inner = false;
                    CojectoException = e;
                }
                return inner;
            }
            );
        }

        private async Task<bool> TerminateCojectoAsync()
        {
            return await Task.Run(() =>
            {
                if (Instance != null)
                {
                    Instance.Dispose();
                    Instance = null;
                }
                return true;
            }
            );
        }

        private async Task<bool> LoadCojectoAsync()
        {
            return await SemanticDatabase.LoadTtl(TestTtl, CojectoSemanticSession.OrandoBaseUri);
        }

        private async Task<List<String>> QueryPropertyDomainAsync()
        {
            String query = @"
                    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
                    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
                    SELECT ?s
                    WHERE { ?s rdfs:domain rdf:Property }
                ";
            List<String> subjects = new List<String>();
            await SemanticDatabase.Query(query, (bindings, values, types, languages, datatypes) =>
            {
                for (int i = 0; i < bindings.Length; i++)
                {
                    String v = values[i];
                    String b = bindings[i];
                    if (b == "s")
                        subjects.Add(v);
                }
                return false;
            }
            );
            return subjects;
        }

        private async Task<List<String>> QuerySubclassOfLabelAsync()
        {
            String query = @"
                    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
                    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
                    SELECT ?s
                    WHERE { ?s rdfs:label ""subClassOf""@en }
                ";
            List<String> subjects = new List<String>();
            await SemanticDatabase.Query(query, (bindings, values, types, languages, datatypes) =>
            {
                for (int i = 0; i < bindings.Length; i++)
                {
                    String v = values[i];
                    String b = bindings[i];
                    if (b == "s")
                        subjects.Add(v);
                }
                return false;
            }
            );
            return subjects;
        }

        /// <summary>
        /// Initializes the singleton Application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        protected override async void OnLaunched(LaunchActivatedEventArgs args)
        {
            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();
                //Associate the frame with a SuspensionManager key                                
                SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

                if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    // Restore the saved session state only when appropriate
                    try
                    {
                        await SuspensionManager.RestoreAsync();
                    }
                    catch (SuspensionManagerException)
                    {
                        //Something went wrong restoring state.
                        //Assume there is no state and continue
                    }
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }
            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
                {
                    throw new Exception("Failed to create initial page");
                }
            }
            // Initialize Cojecto if needed
            if (Instance == null)
            {
                bool success = await InitializeCojectoAsync();
                if (!success)
                {
                    // Cojecto initialization failed, look in CojectoException for more information
                }
                else
                {
                    await LoadCojectoAsync();
                }
            }

            List<String> domains = await QueryPropertyDomainAsync();
            bool found = domains.Count == 3;
            List<String> labels = await QuerySubclassOfLabelAsync();
            found = labels.Count == 1;
            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private async void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            await SuspensionManager.SaveAsync();
            await TerminateCojectoAsync();
            deferral.Complete();
        }

        static String TestTtl = @"
        @prefix : <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

rdf:type     a rdf:Property;
     :comment ""Indicates membership of a class"";
     :domain :Resource;
     :isDefinedBy rdf:;
     :label ""type""@en;
     :range :Class .

rdf:Alt     a :Class;
     :comment ""A collection of alternatives.""@en;
     :isDefinedBy rdf:;
     :label ""Alt""@en;
     :subClassOf :Container .

rdf:Bag     a :Class;
     :comment ""An unordered collection.""@en;
     :isDefinedBy rdf:;
     :label ""Bag""@en;
     :subClassOf :Container .

rdf:Property     a :Class;
     :comment ""The concept of a property."";
     :isDefinedBy rdf:;
     :label ""Property""@en;
     :subClassOf :Resource .

rdf:Seq     a :Class;
     :comment ""An ordered collection.""@en;
     :isDefinedBy rdf:;
     :label ""Seq""@en;
     :subClassOf :Container .

rdf:Statement     a :Class;
     :comment ""The class of RDF statements."";
     :isDefinedBy rdf:;
     :label ""Statement""@en;
     :subClassOf :Resource .

rdf:object     a rdf:Property;
     :comment ""The object of an RDF statement."";
     :domain rdf:Statement;
     :isDefinedBy rdf:;
     :label ""object""@en .

rdf:predicate     a rdf:Property;
     :comment ""the predicate of an RDF statement."";
     :domain rdf:Statement;
     :isDefinedBy rdf:;
     :label ""predicate""@en;
     :range rdf:Property .

rdf:subject     a rdf:Property;
     :comment ""The subject of an RDF statement."";
     :domain rdf:Statement;
     :isDefinedBy rdf:;
     :label ""subject""@en;
     :range :Resource .

rdf:value     a rdf:Property;
     :comment ""Identifies the principal value (usually a string) of a property when the property value is a structured resource"";
     :domain :Resource;
     :isDefinedBy rdf:;
     :label ""value""@en .

:     :seeAlso <http://www.w3.org/2000/01/rdf-schema-more> .

:Class     a :Class;
     :comment ""The concept of Class"";
     :isDefinedBy :;
     :label ""Class""@en;
     :subClassOf :Resource .

:Container     a :Class;
     :comment ""This represents the set Containers."";
     :isDefinedBy :;
     :label ""Container""@en;
     :subClassOf :Resource .

:ContainerMembershipProperty     a :Class;
     :comment ""The container membership properties, rdf:1, rdf:2, ..., all of which are sub-properties of 'member'."";
     :isDefinedBy :;
     :label ""ContainerMembershipProperty""@en;
     :subClassOf rdf:Property .

:Literal     a :Class;
     :comment ""This represents the set of atomic values, eg. textual strings."";
     :isDefinedBy :;
     :label ""Literal""@en .

:Resource     a :Class;
     :comment ""The class resource, everything."";
     :isDefinedBy :;
     :label ""Resource""@en .

:comment     a rdf:Property;
     :comment ""Use this for descriptions"";
     :domain :Resource;
     :isDefinedBy :;
     :label ""comment""@en;
     :range :Literal .

:domain     a rdf:Property;
     :comment ""A domain class for a property type"";
     :domain rdf:Property;
     :isDefinedBy :;
     :label ""domain""@en;
     :range :Class .

:isDefinedBy     a rdf:Property;
     :comment ""Indicates the namespace of a resource"";
     :domain :Resource;
     :isDefinedBy :;
     :label ""isDefinedBy""@en;
     :range :Resource;
     :subPropertyOf :seeAlso .

:label     a rdf:Property;
     :comment ""Provides a human-readable version of a resource name."";
     :domain :Resource;
     :isDefinedBy :;
     :label ""label""@en;
     :range :Literal .

:member     a rdf:Property;
     :comment ""a member of a container"";
     :domain :Container;
     :isDefinedBy :;
     :label ""member""@en .

:range     a rdf:Property;
     :comment ""A range class for a property type"";
     :domain rdf:Property;
     :isDefinedBy :;
     :label ""range""@en;
     :range :Class .

:seeAlso     a rdf:Property;
     :comment ""A resource that provides information about the subject resource"";
     :domain :Resource;
     :isDefinedBy :;
     :label ""seeAlso""@en;
     :range :Resource .

:subClassOf     a rdf:Property;
     :comment ""Indicates membership of a class"";
     :domain :Class;
     :isDefinedBy :;
     :label ""subClassOf""@en;
     :range :Class .

:subPropertyOf     a rdf:Property;
     :comment ""Indicates specialization of properties"";
     :domain rdf:Property;
     :isDefinedBy :;
     :label ""subPropertyOf""@en;
     :range rdf:Property .
";

    }
}
